home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
Libraries
/
VideoToolbox 95.04.18
/
Demos
/
NoiseVBL.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-11
|
8KB
|
238 lines
/*
NoiseVBL.c
There are two ways to synchronize your program to a video display. Apple
recommends using the vertical blanking level interrupt (VBL). That approach is
illustrated here. The other approach, which I normally prefer, is to use a side
effect of loading the video cards clut: most video drivers don't return until
the vertical blanking interval. The interrupt approach is more or less
guaranteed to work on all video cards, since Apple more or less requires that it
be supported by all video drivers. Its only disadvantage is that
interrupts must be enabled. I have the impression--though I haven't collected
hard data--that interrupts steal a lot of time, depending on how busy AppleTalk,
the disk, SCSI, keyboard, mouse, and even miscellaneous INITs that are driven by
VBL interrupts. So when I really want to crank, to display movies, I prefer to
use SetPriority(7) to temporarily suspend all interrupts. In that case the
video-driver side effect approach is your only choice. However, be warned that
some video drivers don't wait, and others take more than a frame to load the
clut, so do some careful timing before you rely on it, i.e. run TimeVideo.
This program shows a noise movie, a dynamic random checkerboard, at 66.7 Hz!
Each frame is displayed by calling CopyWindows.c. After showing each frame once
NoiseVBL cycles through them again. Thus to get a true impression of white noise you
need to compute lots of noise frames, e.g. 100. This may require several
megabytes, depending what your screen's pixel depth is set to.
HISTORY:
11/23/88 Denis Pelli wrote it.
4/20/91 I updated this. It now has essentially no hardware dependencies. It does
require that the video be in a slot, since it uses slot interrupts. A basic
problem with the scheme is that interrupts seem to be shut out while the
interrupt service routines are running. As a result the timing information is
inaccurate since many 1 ms interrupts are lost during the call to
CopyBitsQuickly(). I think the only solution to this would be to put the time
consuming call to CopyBitsQuickly back in the main program, waiting for the
interrupt service routine to give it permission to start each new frame.
4/15/92 Removed all bugs and updated to work with THINK C 5. Now properly set A5
in the interrupt service routines, using the method suggested in Tech Note 180.
I removed the old attempt to disable AppleTalk, which now seems to hang up when
it attempts to reenable AppleTalk. I also removed the clut manipulations.
4/16/92 Put the noise in its own window. Run essentially continuously until
user quits.
8/19/92 Replaced obsolete TimeIt.c by new Timer.c. Timing seems to be fine now.
8/22/92 dgp Moved most of the interrupt service routine code to
VBLInterruptServiceRoutine.c. Just for fun, I made the program use an alternate monitor,
if available. Fixed a small bug that prevented display of noise on an alternate
monitor if it had a different pixelSize than the main screen.
9/15/92 dgp Updated to use new Timer.c.
2/18/93 dgp Added fpu test.
2/20/93 dgp Call Require(), increased stack space.
7/7/93 dgp replaced call to CopyBitsQuickly by CopyBits, for compatibility with
Radius PowerView.
3/19/94 dgp updated the call to Shuffle(), adding new required argument.
5/30/94 dgp call StackGrow() before calling NoiseVBL().
7/27/94 dgp changed "thePort" to &qd.thePort, for broader compatibility, as suggested by
Marty Wachter.
9/5/94 dgp removed assumption in printf's that int==short.
11/6/94 dgp updated to demonstrate use of GWorld and CopyWindows.
*/
#include "VideoToolbox.h"
#include <assert.h>
#if (THINK_C || THINK_CPLUS)
#include <console.h>
#endif
#if __MWERKS__
#include <SIOUX.h>
#endif
#if UNIVERSAL_HEADERS
#include <LowMem.h>
#else
#define LMGetMBarHeight() (* (short *) 0x0BAA)
#define LMSetMBarHeight(MBarHeightValue) ((* (short *) 0x0BAA) = (MBarHeightValue))
#endif
/* The user may wish to adjust these. WIDTH should be a multiple of 4. */
#define MAXFRAMES 200 // number of frames in movie
#define WIDTH 256 /* width of displayed noise movie, in pixels */
#define HEIGHT 256 /* height of displayed noise movie, in pixels */
#define RANDOMPHASE 1 /* randomly shift each movie frame */
/* This is just for reference. The original is in VideoToolbox.h
struct VBLTaskAndA5 {
volatile VBLTask vbl;
long ourA5;
void (*subroutine)(struct VBLTaskAndA5 *vblData);
GDHandle device;
long slot;
volatile long newFrame; // Boolean
volatile long framesLeft; // count down to zero
long framesDesired;
void *ptr; // use this for whatever you want
};
typedef struct VBLTaskAndA5 VBLTaskAndA5;
*/
void main(void);
void NoiseVBL(void);
void main()
{
StackGrow(20000);
Require(gestalt8BitQD);
NoiseVBL();
}
void NoiseVBL()
{
int i,error,dx=32,dy=32,dt=1;
long frames;
unsigned long seed;
double s;
static char string[64];
Rect r;
short noiseI=0,noiseN=MAXFRAMES;
GWorldPtr noiseImage[MAXFRAMES];
short noiseSequence[MAXFRAMES]; /* shuffled sequence */
VBLTaskAndA5 noiseVBL;
WindowPtr window,oldPort;
EventRecord event;
Timer *timer;
int screen;
ColorTable **ct;
assert(StackSpace()>5000);
GetDateTime(&seed);
srand(seed);
/* INITIALIZE QuickDraw */
#if (THINK_C || THINK_CPLUS)
console_options.nrows = 3;
printf("\n");
#elif __MWERKS__
SIOUXSettings.toppixel=LMGetMBarHeight()+19; // allow for menu bar and title bar
SIOUXSettings.leftpixel=1;
SIOUXSettings.rows=3;
SIOUXSettings.autocloseonquit=0;
SIOUXSettings.showstatusline=0;
SIOUXSettings.asktosaveonclose=0;
printf("\n");
#else
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitCursor();
#endif
do{
if(GetScreenDevice(1)!=NULL)screen=ChooseScreen(1,"Which screen?");
else screen=0;
noiseVBL.device=GetScreenDevice(screen);
}while(noiseVBL.device==NULL);
printf("Enter width of check in pixels (%d)?",dx);
gets(string);
sscanf(string,"%d",&dx);
printf("%d\n",dx);
printf("Enter height of check in pixels (%d)?",dy);
gets(string);
sscanf(string,"%d",&dy);
printf("%d\n",dy);
printf("Enter duration of check in frames (%d)?",dt);
gets(string);
sscanf(string,"%d",&dt);
printf("%d\n",dt);
GetPort(&oldPort);
SetRect(&r,0,0,WIDTH,WIDTH);
CenterRectInRect(&r,&(*noiseVBL.device)->gdRect);
OffsetRect(&r,-(r.left%32),0);
window=NewCWindow(NULL,&r,"\pComputing noise ...",1,noGrowDocProc,(WindowPtr) -1L,0,0L);
SetPort(window);
ct=(**(**noiseVBL.device).gdPMap).pmTable;
for(i=0;i<MAXFRAMES;i++){
error=NewGWorld(&noiseImage[i],0,&r,ct,noiseVBL.device,keepLocal+noNewDevice+useTempMem);
if(error)error=NewGWorld(&noiseImage[i],0,&r,ct,noiseVBL.device,keepLocal+noNewDevice);
if(error)break;
error=NoiseFill(noiseImage[i],&noiseImage[i]->portRect,dx,dy,RANDOMPHASE);
LockPixels(noiseImage[i]->portPixMap);
if(error)break;
}
noiseN=i;
SetPort(oldPort);
printf("Using %d different frames of noise.\n",(int)noiseN);
for(i=0;i<noiseN;i++)noiseSequence[i]=i; /* initialize sequence */
FlushEvents(-1L,0);
printf("Hit any key to quit.\n");
SetWTitle(window,"\pNoiseVBL");
timer=NewTimer();
do{
SelectWindow(window);
Shuffle(noiseSequence,noiseN,sizeof(*noiseSequence)); /* shuffle the images */
noiseI=0;
noiseVBL.subroutine=NULL; // use default
error=VBLInstall(&noiseVBL,noiseVBL.device,MAXFRAMES);
if(error)PrintfExit("VBLInstall: error %d\n",error);
noiseVBL.vbl.vblCount=1; // Enable interrupt service routine
while(!noiseVBL.newFrame) ; // wait for first frame
noiseVBL.newFrame=0;
while(!noiseVBL.newFrame) ; // wait for second frame
StartTimer(timer);
frames=noiseVBL.framesLeft;
while(noiseVBL.framesLeft>1){
if(noiseVBL.newFrame){
noiseVBL.newFrame=0;
CopyWindows(noiseImage[noiseSequence[noiseI]],(CWindowPtr)window
,&noiseImage[0]->portRect,&window->portRect,srcCopy,NULL);
if(noiseVBL.framesLeft%dt==0){
noiseI++;
if(noiseI>=noiseN) {
Shuffle(noiseSequence,noiseN,sizeof(*noiseSequence));
noiseI=0;
}
}
}
}
while(!noiseVBL.newFrame) ; // wait for last frame
s=StopTimer(timer)/1000000.;
frames-=noiseVBL.framesLeft;
VBLRemove(&noiseVBL);
printf("%.1f Hz \r",frames/s);
}while(!GetNextEvent(keyDown+keyUp+mouseDown,&event));
FlushEvents(-1L,0);
for (i=0;i<noiseN;i++) DisposeGWorld(noiseImage[i]);
DisposeWindow(window);
DisposeTimer(timer);
#if (THINK_C || THINK_CPLUS)
abort();
#elif __MWERKS__
SIOUXSettings.autocloseonquit=1;
#endif
}